package cn.stimd.spring.beans.factory.config;

import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 依赖描述符，可以是字段或方法参数
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class DependencyDescriptor {
    private final Class<?> declaringClass;  //声明字段或方法的类
    private Class<?> containingClass;       //依赖项的具体类，不一定等价于字段或方法参数的类型，可能是子类型
    private Field field;
    private MethodParameter methodParameter;
    private int nestingLevel = 1;           //类型的嵌套层级，简单类型的层级为1，List等泛型集合的层级大于1，具体取决于泛型类型
    private final boolean required;         //依赖项是否必须存在
    private volatile Annotation[] fieldAnnotations;
    private volatile ResolvableType resolvableType;     //如果依赖的类型是集合或数组，则需要得到元素的类型

    public DependencyDescriptor(Field field, boolean required) {
        this.field = field;
        this.required = required;
        this.declaringClass = field.getDeclaringClass();
        this.fieldAnnotations = this.field.getDeclaredAnnotations();
    }

    public DependencyDescriptor(MethodParameter methodParameter, boolean required){
        this.declaringClass = methodParameter.getDeclaringClass();
        this.required = required;
        this.methodParameter = methodParameter;
    }


    public DependencyDescriptor(DependencyDescriptor original) {
        this.declaringClass = original.declaringClass;
        this.containingClass = original.containingClass;
        this.field = original.field;
        this.methodParameter = original.methodParameter;
        this.fieldAnnotations = original.fieldAnnotations;
        this.required = original.required;
        this.nestingLevel = original.nestingLevel;
    }


    /**
     * 从字段或方法参数上获取依赖的类型（不考虑多层嵌套的情况）
     */
    public Class<?> getDependencyType(){
        return this.field != null ? this.field.getType() : this.methodParameter.getNestedParameterType();
    }


    //获取字段或方法参数上的注解
    public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
        return (this.field != null ? this.field.getAnnotation(annotationType) :
                this.methodParameter.getParameterAnnotation(annotationType));
    }


    public ResolvableType getResolvableType() {
        ResolvableType resolvableType = this.resolvableType;
        if (resolvableType == null) {
            resolvableType = (this.field != null ?
                    ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
                    ResolvableType.forMethodParameter(this.methodParameter));
            this.resolvableType = resolvableType;
        }
        return resolvableType;
    }

    public void setContainingClass(Class<?> containingClass) {
        this.containingClass = containingClass;
    }


    public Annotation[] getAnnotations() {
        return this.field != null ? this.fieldAnnotations : this.methodParameter.getParameterAnnotations();
    }

    public Field getField() {
        return field;
    }

    public boolean isRequired() {
        return required;
    }

    public String getDependencyName() {
        return (this.field != null ? this.field.getName() : this.methodParameter.getParameterName());
    }


    public void increaseNestingLevel() {
        this.nestingLevel++;
        this.resolvableType = null;
        if (this.methodParameter != null) {
            this.methodParameter.increaseNestingLevel();
        }
    }
}
